﻿using System;
using System.IO;

namespace tuimg.element
{
    using tuimg.helpers;
    using tuimg.structs;

    public class TuimgFont : IDisposable
    {
        internal struct_ttf_head head;
        internal struct_ttf_table_record[] tables;
        internal struct_ttf_head_info head_info;
        internal object name_table;
        internal struct_ttf_cmap_head cmap_head;
        internal struct_ttf_loca loca;
        internal struct_ttf_hhea hhea;
        internal object maxp;
        internal long name_table_storageOffset = -1;
        internal long cmap_table_offset = -1;
        internal long cmap_offset = -1;
        internal bool is_long_format = false;
        internal ushort numGlyphs { get; set; }
        public string FontFamilyName { get; internal set; }
        public string FontName { get; internal set; }
        public string Version { get; internal set; }
        internal Stream ttf_stream;
        public TuimgFont(string path)
        {
            head = new struct_ttf_head { };
            ttf_stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            head.sfntVersion = helper.Readuint2(ttf_stream);
            if (!check_isttf()) throw new ArgumentException("not ttf file.");

            head.numTables = helper.Readushort2(ttf_stream);
            head.searchRange = helper.Readushort2(ttf_stream);
            head.entrySelector = helper.Readushort2(ttf_stream);
            head.rangeShift = helper.Readushort2(ttf_stream);

            tables = new struct_ttf_table_record[head.numTables];
            for (var i = 0; i < tables.Length; i++)
            {
                tables[i] = new struct_ttf_table_record { };
                tables[i].tableTag = helper.Readchars(ttf_stream, 4);
                tables[i].checksum = helper.Readuint2(ttf_stream);
                tables[i].offset = helper.Readuint2(ttf_stream);
                tables[i].length = helper.Readuint2(ttf_stream);
            }

            head_info = read_headinfo(ttf_stream);
            hhea = read_hhea(ttf_stream);
            maxp = read_maxp(ttf_stream);
            loca = read_loca(ttf_stream);
            name_table = read_name(ttf_stream);
            FontFamilyName = read_name_record(ttf_stream, enum_ttf_names.Font_Family_name);
            FontName = read_name_record(ttf_stream, enum_ttf_names.Full_font_name);
            Version = read_name_record(ttf_stream, enum_ttf_names.Version);

            cmap_head = read_cmap(ttf_stream);
        }
        public bool is_support_text(ushort text)
        {
            return calc_loca_index(text) != -1;
        }
        public TuimgPath ReadPath(ushort text)
        {
            var a = calc_glyf_index(text);
            var glyf = read_glyf(ttf_stream, a);

            var path = new TuimgPath(glyf.PtsCount + glyf.numberOfContours);

            var x = 0;
            var y = 0;
            for (int i = 0; i < glyf.PtsCount; i++)
            {
                if (glyf.xCoordinates[i] < x) x = glyf.xCoordinates[i];
                if (glyf.yCoordinates[i] < y) y = glyf.yCoordinates[i];
            }
            if(glyf.yMin < y) y = glyf.yMin;
            if (glyf.xMin< x) x = glyf.xMin;
            x = -x;
            y = -y;
            if (x < 0) x = 0;
            if (y < 0) y = 0;
            x += 2; y += 2;
            // 移动画布从原点之后开始
            glyf.xMin += (short)x;
            glyf.yMin += (short)y;
            glyf.xMax += (short)(x + 2);
            glyf.yMax += (short)(y + 2);
            path.SetViewBox(glyf);

            glyf.xCoordinates[0] += (short)x;
            glyf.yCoordinates[0] += (short)y;
            for (var i = 1; i < glyf.PtsCount; i++)
            {
                glyf.xCoordinates[i] += glyf.xCoordinates[i - 1];
                glyf.yCoordinates[i] += glyf.yCoordinates[i - 1];
            }

            ushort end = 0;
            for (var i= 0; i< glyf.numberOfContours; i++)
            {
                var start = 0;
                if (i > 0) start = glyf.endPtsOfContours[i - 1]+1;
                end = glyf.endPtsOfContours[i];
                var len = end - start;

                path.MovePoint(glyf.xCoordinates[start], glyf.yCoordinates[start]);
                for (var j = 1; j <= len; j++)
                {
                    if ((glyf.flags[start+j] & 1) == 1)
                        path.Line(glyf.xCoordinates[start + j], glyf.yCoordinates[start + j]);
                    else {
                        while (j < len && (glyf.flags[start + j] & 1) == 0)
                        {
                            var j2 = (j + 1) % len;
                            if (j <len && (glyf.flags[start + j2] & 1) == 1)
                            {
                                path.Quadratic_Bezier_Curve(glyf.xCoordinates[start + j], glyf.yCoordinates[start + j], glyf.xCoordinates[start + j+1], glyf.yCoordinates[start + j + 1]);
                                break;
                            }
                            if(j+1 < len && (glyf.flags[start + j +1] & 1) == 0)
                                path.Quadratic_Bezier_Curve(glyf.xCoordinates[start + j], glyf.yCoordinates[start + j], (glyf.xCoordinates[start + j]+glyf.xCoordinates[start + j2])>>1, (glyf.yCoordinates[start + j] + glyf.yCoordinates[start + j2]) >> 1);
                            else
                                path.Quadratic_Bezier_Curve(glyf.xCoordinates[start + j], glyf.yCoordinates[start + j], glyf.xCoordinates[start + j2], glyf.yCoordinates[start + j2]);
                            j++;
                        }

                    }
                }
            }
            path.End();

            path.Flip_vertical();
            return path;
        }
        // 检查是否是ttf
        internal bool check_isttf() { return head.sfntVersion == 0x00010000 || head.sfntVersion == 0x74727565; }
        internal uint calc_checksum() { return 0; }
        internal struct_ttf_glyf_head read_glyf(Stream sr, uint text_index)
        {
            var glyf = find_table_record("glyf");
            sr.Position = glyf.offset + text_index;
            var glyf_head = new struct_ttf_glyf_head { };

            glyf_head.numberOfContours = helper.Readshort2(sr);
            glyf_head.xMin = helper.Readshort2(sr);
            glyf_head.yMin = helper.Readshort2(sr);
            glyf_head.xMax = helper.Readshort2(sr);
            glyf_head.yMax = helper.Readshort2(sr);
            if (glyf_head.numberOfContours >= 0)
            {
                glyf_head.endPtsOfContours = new ushort[glyf_head.numberOfContours];
                for (var i = 0; i < glyf_head.numberOfContours; i++) glyf_head.endPtsOfContours[i] = helper.Readushort2(sr);
                glyf_head.PtsCount = glyf_head.endPtsOfContours[glyf_head.numberOfContours - 1] + 1;

                glyf_head.instructionLength = helper.Readushort2(sr);
                glyf_head.instructions = new byte[glyf_head.instructionLength];
                for (int i = 0; i < glyf_head.instructionLength; i++) glyf_head.instructions[i] = helper.Readbyte(sr);

                glyf_head.flags = new byte[glyf_head.PtsCount];
                glyf_head.xCoordinates = new short[glyf_head.PtsCount];
                glyf_head.yCoordinates = new short[glyf_head.PtsCount];
                for (int i = 0; i < glyf_head.PtsCount; i++)
                {
                    glyf_head.flags[i] = helper.Readbyte(sr);
                    if ((glyf_head.flags[i] & 0x8) == 0x8) for (var j = helper.Readbyte(sr); j > 0; j--) glyf_head.flags[++i] = glyf_head.flags[i - 1];
                }
                for (int i = 0; i < glyf_head.PtsCount; i++)
                {
                    if ((glyf_head.flags[i] & 0x10) == 0x10)
                    {
                        if ((glyf_head.flags[i] & 0x2) == 0x2) glyf_head.xCoordinates[i] = helper.Readbyte(sr);
                    }
                    else
                    {
                        if ((glyf_head.flags[i] & 0x2) == 0x2) glyf_head.xCoordinates[i] -= helper.Readbyte(sr);
                        else glyf_head.xCoordinates[i] = helper.Readshort2(sr);
                    }
                }
                for (int i = 0; i < glyf_head.PtsCount; i++)
                {
                    if ((glyf_head.flags[i] & 0x20) == 0x20)
                    {
                        if ((glyf_head.flags[i] & 0x04) == 0x04) glyf_head.yCoordinates[i] = helper.Readbyte(sr);
                    }
                    else
                    {
                        if ((glyf_head.flags[i] & 0x04) == 0x04) glyf_head.yCoordinates[i] -= helper.Readbyte(sr);
                        else glyf_head.yCoordinates[i] = helper.Readshort2(sr);
                    }
                }
            }
            else if (glyf_head.numberOfContours == -1)
            {
                ushort flags;
                int components_len = 0;
                do
                {
                    flags = helper.Readushort2(sr);

                    if ((flags & 0x01) != 0)  sr.Position += 4;
                    else  sr.Position += 2;
                    if ((flags & 0x08) != 0) sr.Position += 2;
                    else if ((flags & 0x40) != 0) sr.Position += 4;
                    else if ((flags & 0x80) != 0) sr.Position += 8;
                    components_len++;
                } while ((flags & 0x20) != 0);

                var ix = 0;
                var components = new struct_ttf_glyf_component[components_len];
                do { 
                    struct_ttf_glyf_component component;
                    component = new struct_ttf_glyf_component { xscale = 1.0M, yscale = 1.0M, scale01 = 0.0M, scale10 = 0.0M };
                    flags= component.flags = helper.Readushort2(sr);
                    component.glyphIndex = helper.Readushort2(sr);

                    if ((component.flags & 0x01) != 0)
                    {
                        component.argument1 = helper.Readushort2(sr);
                        component.argument2 = helper.Readushort2(sr);
                    }
                    else
                    {
                        component.argument1 = helper.Readbyte(sr);
                        component.argument2 = helper.Readbyte(sr);
                    }
                    if ((component.flags & 0x02) != 0)
                    {
                        component.xtranslate = component.argument1;
                        component.ytranslate = component.argument2;
                    }
                    else
                    {
                        component.point1 = component.argument1;
                        component.point2 = component.argument2;
                    }
                    if ((component.flags & 0x08) != 0)
                    {
                        var i = helper.Readshort2(sr);
                        component.xscale = i / 0x4000;
                        component.yscale = i / 0x4000;
                    }
                    else if ((component.flags & 0x40) != 0)
                    {
                        component.xscale = helper.Readshort2(sr) / 0x4000;
                        component.yscale = helper.Readshort2(sr) / 0x4000;
                    }
                    else if ((component.flags & 0x80) != 0)
                    {
                        component.xscale = helper.Readshort2(sr) / 0x4000;
                        component.scale01 = helper.Readshort2(sr) / 0x4000;
                        component.scale10 = helper.Readshort2(sr) / 0x4000;
                        component.yscale = helper.Readshort2(sr) / 0x4000;
                    }
                    components[ix++] = component;
		        } while ((flags & 0x20)!=0);
                glyf_head.components = components;
            }
            else throw new NotSupportedException("not support Composite glyph.");

            return glyf_head;
        }
        internal uint calc_glyf_index(ushort text)
        {
            var ix = calc_loca_index(text);
            if (ix != -1) return loca.offsets[ix];
            throw new ArgumentNullException($"cannot found text glyf {text}");
        }
        internal struct_ttf_loca read_loca(Stream sr)
        {
            var loca_record = find_table_record("loca");
            sr.Position = loca_record.offset;
            var loca = new struct_ttf_loca { };
            loca.length = (uint)(numGlyphs + 1);
            loca.offsets = new uint[loca.length];
            
            if(head_info.indexToLocFormat == 1) for(var i = 0; i< loca.length;i++) loca.offsets[i] = helper.Readuint2(sr);
            else for(var i = 0; i< loca.length;i++) loca.offsets[i] = (uint)(helper.Readushort2(sr)*2);
            return loca;
        }
        internal object read_maxp(Stream sr)
        {
            var maxp_record = find_table_record("maxp");
            sr.Position = maxp_record.offset;
            var version = helper.Read16Dot16(sr);
            switch (version)
            {
                case 0.5M:
                    var maxp_v0_5 = new struct_ttf_maxp_v0_5 { };
                    maxp_v0_5.version = version;
                    numGlyphs = maxp_v0_5.numGlyphs = helper.Readushort2(sr);
                    return maxp_v0_5;
                case 1.0M:
                    var maxp_v1_0 = new struct_ttf_maxp_v1_0 { };
                    maxp_v1_0.version = version;
                    numGlyphs = maxp_v1_0.numGlyphs = helper.Readushort2(sr);
                    maxp_v1_0.maxPoints = helper.Readushort2(sr);
                    maxp_v1_0.maxContours = helper.Readushort2(sr);
                    maxp_v1_0.maxCompositePoints = helper.Readushort2(sr);
                    maxp_v1_0.maxCompositeContours = helper.Readushort2(sr);
                    maxp_v1_0.maxZones = helper.Readushort2(sr);
                    maxp_v1_0.maxTwilightPoints = helper.Readushort2(sr);
                    maxp_v1_0.maxStorage = helper.Readushort2(sr);
                    maxp_v1_0.maxFunctionDefs = helper.Readushort2(sr);
                    maxp_v1_0.maxInstructionDefs = helper.Readushort2(sr);
                    maxp_v1_0.maxStackElements = helper.Readushort2(sr);
                    maxp_v1_0.maxSizeOfInstructions = helper.Readushort2(sr);
                    maxp_v1_0.maxComponentElements = helper.Readushort2(sr);
                    maxp_v1_0.maxComponentDepth  = helper.Readushort2(sr);
                    return maxp_v1_0;
            }
            throw new ArgumentException("unknown maxp.");
        }
        // -1 is not found
        internal int calc_loca_index(ushort text)
        {
            foreach (var cmap_table in cmap_head.encodingTables)
            if (cmap_table is struct_ttf_cmap_encoding_table_f4 cmap_table_f4)
            {
                for(var i = 0; i< cmap_table_f4.segCount; i++)
                {
                    if (cmap_table_f4.startCode[i] <= text && cmap_table_f4.endCode[i] >= text)
                    {
                        if (cmap_table_f4.idRangeOffset[i] == 0) return text + cmap_table_f4.idDelta[i];
                        //从流中获取
                        //var glyphOffset = cmap_table_f4.glyphIdOffset + ((cmap_table_f4.idRangeOffset[i] / 2) + (text - cmap_table_f4.startCode[i]) + (i - cmap_table_f4.segCount)) * 2;
                        //ttf_stream.Position = glyphOffset;
                        //var index = helper.Readushort2(ttf_stream);
                        //if (index != 0)
                        //    {
                        //        index += (ushort)cmap_table_f4.idDelta[i];
                        //        index %= 65535;
                        //        return index;
                        //    }
                        return cmap_table_f4.glyphIdArray[(cmap_table_f4.idRangeOffset[i]/2) +  (text - cmap_table_f4.startCode[i]) + (i - cmap_table_f4.segCount)] + cmap_table_f4.idDelta[i];
                    }
                }

            }
            return -1;
        }
        internal object read_cmap_table(Stream sr, struct_ttf_cmap_encoding_record encodingRecord)
        {
            var cmap_tables = new object[cmap_head.numTables];
            sr.Position = cmap_offset + encodingRecord.subtableOffset;
            var format = helper.Readushort2(sr);
            switch (format)
            {
                case 0:
                    var table_f0 = new struct_ttf_cmap_encoding_table_f0 { };
                    table_f0.format = format;
                    table_f0.length = helper.Readushort2(sr);
                    table_f0.language = helper.Readushort2(sr);
                    return table_f0;
                case 4:
                    var table_f4 = new struct_ttf_cmap_encoding_table_f4 { };
                    table_f4.format = format;
                    table_f4.length = helper.Readushort2(sr);
                    table_f4.language = helper.Readushort2(sr);
                    table_f4.segCountX2 = helper.Readushort2(sr);
                    table_f4.segCount = (ushort)(table_f4.segCountX2 / 2);
                    table_f4.searchRange = helper.Readushort2(sr);
                    table_f4.entrySelector = helper.Readushort2(sr);
                    table_f4.rangeShift = helper.Readushort2(sr);

                    table_f4.endCode = new ushort[table_f4.segCount];
                    for (var j = 0; j < table_f4.segCount; j++) table_f4.endCode[j] = helper.Readushort2(sr);
                    table_f4.reservedPad = helper.Readushort2(sr);
                    table_f4.startCode = new ushort[table_f4.segCount];
                    for (var j = 0; j < table_f4.segCount; j++) table_f4.startCode[j] = helper.Readushort2(sr);
                    table_f4.idDelta = new short[table_f4.segCount];
                    for (var j = 0; j < table_f4.segCount; j++) table_f4.idDelta[j] = helper.Readshort2(sr);
                    table_f4.idRangeOffset = new ushort[table_f4.segCount];
                    for (var j = 0; j < table_f4.segCount; j++) table_f4.idRangeOffset[j] = helper.Readushort2(sr);
                    table_f4.glyphIdOffset = sr.Position;
                    table_f4.glyphIdArray = new ushort[numGlyphs];
                    for (var j = 0; j < numGlyphs; j++) table_f4.glyphIdArray[j] = helper.Readushort2(sr);
                    return table_f4;
            }

            return new struct_ttf_cmap_encoding_table_f0 { };
        }
        internal struct_ttf_cmap_head read_cmap(Stream sr)
        {
            var cmap = find_table_record("cmap");
            sr.Position = cmap_offset = cmap.offset;
            var cmap_head = new struct_ttf_cmap_head { };
            cmap_head.version = helper.Readushort2(sr);
            cmap_head.numTables = helper.Readushort2(sr);

            cmap_head.encodingRecords = new struct_ttf_cmap_encoding_record[cmap_head.numTables];
            for(var i =0; i< cmap_head.numTables; i++)
            {
                cmap_head.encodingRecords[i] = new struct_ttf_cmap_encoding_record { };
                cmap_head.encodingRecords[i].platformID = helper.Readushort2(sr);
                cmap_head.encodingRecords[i].encodingID = helper.Readushort2(sr);
                cmap_head.encodingRecords[i].subtableOffset = helper.Readuint2(sr);
            }

            cmap_table_offset = sr.Position;

            cmap_head.encodingTables = new object[cmap_head.numTables];
            for(var i = 0; i< cmap_head.numTables; i++) cmap_head.encodingTables[i]= read_cmap_table(sr, cmap_head.encodingRecords[i]);
            return cmap_head;
        }
        internal struct_ttf_hhea read_hhea(Stream sr)
        {
            var hhea_record = find_table_record("hhea");
            var hheader = new struct_ttf_hhea { };

            hheader.version = helper.Readuint2(sr);
            hheader.ascender = helper.Readshort2(sr);
            hheader.descender = helper.Readshort2(sr);

            hheader.lineGap = helper.Readshort2(sr);
            hheader.advanceWidthMax = helper.Readushort2(sr);
            hheader.minLeftSideBearing = helper.Readshort2(sr);
            hheader.minRightSideBearing = helper.Readshort2(sr);
            hheader.xMaxExtent = helper.Readshort2(sr);
            hheader.caretSlopeRise = helper.Readshort2(sr);
            hheader.caretSlopeRun = helper.Readshort2(sr);
            hheader.reserved1 = helper.Readshort2(sr);
            hheader.reserved2 = helper.Readshort2(sr);
            hheader.reserved3 = helper.Readshort2(sr);
            hheader.reserved4 = helper.Readshort2(sr);
            hheader.reserved5 = helper.Readshort2(sr);
            hheader.metricDataFormat = helper.Readshort2(sr);
            hheader.numberOfHMetrics = helper.Readushort2(sr);

            return hheader;
        }
        internal struct_ttf_head_info read_headinfo(Stream sr)
        {
            var head_record = find_table_record("head");
            sr.Position = head_record.offset;
            var head_info = new struct_ttf_head_info { };
            head_info.majorVersion = helper.Readushort2(sr);
            head_info.minorVersion = helper.Readushort2(sr);
            head_info.fontRevision = helper.Readuint2(sr);
            head_info.checksumAdjustment = helper.Readuint2(sr);
            head_info.magicNumber = helper.Readuint2(sr);
            head_info.flags = helper.Readushort2(sr);
            head_info.unitsPerEm = helper.Readushort2(sr);
            head_info.created = helper.Readulong2(sr);
            head_info.modified = helper.Readulong2(sr);
            head_info.xMin = helper.Readshort2(sr);
            head_info.yMin = helper.Readshort2(sr);
            head_info.xMax = helper.Readshort2(sr);
            head_info.yMax = helper.Readshort2(sr);
            head_info.macStyle = helper.Readushort2(sr);
            head_info.lowestRecPPEM = helper.Readushort2(sr);
            head_info.fontDirectionHint = helper.Readshort2(sr);
            head_info.indexToLocFormat = helper.Readshort2(sr);
            head_info.glyphDataFormat = helper.Readshort2(sr);
            return head_info;
        }
        internal string read_name_record(Stream sr, enum_ttf_names name)
        {
            if (name_table_storageOffset == -1) throw new ArgumentNullException("storage offset not calc.");
            var record = find_name_record(name);
            sr.Position = name_table_storageOffset + record.stringOffset;
            return helper.Readchars(sr, record.length);
        }

        internal struct_ttf_name_record find_name_record(enum_ttf_names name)
        {
            if (name_table is struct_ttf_name_v0 name_v0_table) for (var i = 0; i < name_v0_table.count; i++) if (name_v0_table.nameRecords[i].nameID == (ushort)name) return name_v0_table.nameRecords[i];
            else if (name_table is struct_ttf_name_v1 name_v1_table) for (i = 0; i < name_v1_table.count; i++) if (name_v1_table.nameRecords[i].nameID == (ushort)name) return name_v1_table.nameRecords[i];
            throw new ArgumentNullException(name.ToString());
        }
        internal object read_name(Stream sr)
        {
            var name_table_record = find_table_record("name");
            sr.Position = name_table_record.offset;

            var version = helper.Readushort2(sr);

            switch (version)
            {
                case 0: return read_name_v0(sr, name_table_record);
                case 1: return read_name_v1(sr, name_table_record);
            }

            throw new ArgumentNullException("at read name");
        }
        internal struct_ttf_name_v0 read_name_v0(Stream sr, struct_ttf_table_record name_table_record)
        {
            sr.Position = name_table_record.offset + 2;
            var name_tables = new struct_ttf_name_v0 { };
            name_tables.version = 0;
            name_tables.count = helper.Readushort2(sr);
            name_tables.storageOffset = helper.Readushort2(sr);

            name_table_storageOffset = name_table_record.offset + name_tables.storageOffset;

            name_tables.nameRecords = new struct_ttf_name_record[name_tables.count];
            for (var i = 0; i < name_tables.count; i++)
            {
                var record = new struct_ttf_name_record { };
                record.platformID = helper.Readushort2(sr);
                record.encodingID = helper.Readushort2(sr);
                record.languageID = helper.Readushort2(sr);
                record.nameID = helper.Readushort2(sr);
                record.length = helper.Readushort2(sr);
                record.stringOffset = helper.Readushort2(sr);
                name_tables.nameRecords[i] = record;
            }
            return name_tables;
        }
        internal struct_ttf_name_v1 read_name_v1(Stream sr, struct_ttf_table_record name_table_record)
        {
            sr.Position = name_table_record.offset + 2;
            var name_tables = new struct_ttf_name_v1 { };
            name_tables.version = 1;
            name_tables.count = helper.Readushort2(sr);
            name_tables.storageOffset = helper.Readushort2(sr);

            name_table_storageOffset = name_table_record.offset + name_tables.storageOffset;

            name_tables.nameRecords = new struct_ttf_name_record[name_tables.count];
            for (var i = 0; i < name_tables.count; i++)
            {
                var record = new struct_ttf_name_record { };
                record.platformID = helper.Readushort2(sr);
                record.encodingID = helper.Readushort2(sr);
                record.languageID = helper.Readushort2(sr);
                record.nameID = helper.Readushort2(sr);
                record.length = helper.Readushort2(sr);
                record.stringOffset = helper.Readushort2(sr);
                name_tables.nameRecords[i] = record;
            }
            name_tables.langTagCount = helper.Readushort2(sr);
            name_tables.langTagRecords = new struct_ttf_name_langtag[name_tables.langTagCount];
            for(var i=0; i< name_tables.langTagCount; i++)
            {
                var langtag = new struct_ttf_name_langtag { };
                langtag.length = helper.Readushort2(sr);
                langtag.langTagOffset = helper.Readushort2(sr);
                name_tables.langTagRecords[i] = langtag;
            }
            return name_tables;
        }

        internal struct_ttf_table_record find_table_record(string name)
        {
            for (var i = 0; i < head.numTables; i++) if (tables[i].tableTag == name) return tables[i];
            throw new ArgumentNullException(name);
        }

        public void Dispose()
        {
            if (ttf_stream != null)
            {
                ttf_stream.Dispose();
                ttf_stream = null;
            }
        }
    }
}
